/**
  ******************************************************************************
  * @file    m1130_eval.c
  * @author  Alpscale Application Team
  * @version V1.0.0
  * @date    16-October-2013
  * @brief   This file provides
  *            - set of firmware functions to manage Leds, push-button and COM ports
  *            - low level initialization functions for SD card (on SDIO)
  *          available on M1130-EVAL evaluation board from Alpscale.
  ******************************************************************************
  * @attention
  *
  * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
  * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
  * TIME. AS A RESULT, ALPHASCALE SHALL NOT BE HELD LIABLE FOR ANY
  * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
  * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
  * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
  *
  * <h2><center>&copy; COPYRIGHT 2013 Alpscale</center></h2>
  ******************************************************************************
  */

/* Includes ------------------------------------------------------------------*/
#include "m1130_eval.h"
#include "m1130_dma.h"
#include "m1130_i2c.h"
#include "m1130_qspi.h"
#include "m1130_rcc.h"
#include "m1130_gpio.h"
#include "misc.h"
#include "stdio.h"


void QSPI_LowLevel_DeInit(QSPI_TypeDef *QSPIptr)
{
  NVIC_InitTypeDef NVIC_InitStructure;

  QSPI_SetPowerState(QSPIptr, QSPI_PowerState_OFF);
  /*Disable SD interrupt here*/
  if (QSPIptr == QSPI0) {
    NVIC_InitStructure.NVIC_IRQChannel = QSPI0_IRQn;
  } else if (QSPIptr == QSPI1) {
    NVIC_InitStructure.NVIC_IRQChannel = QSPI1_IRQn;
  } else {
    NVIC_InitStructure.NVIC_IRQChannel = QSPI2_IRQn;
  }
  NVIC_InitStructure.NVIC_IRQChannelPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelCmd = DISABLE;
  NVIC_Init(&NVIC_InitStructure);
}

/**
  * @brief Enable the QSPI interrupt line.
  * @retval None
  */
void QSPI_IRQ_Num_Enable(QSPI_TypeDef *QSPIptr)
{
  NVIC_InitTypeDef NVIC_InitStructure;
  if (QSPIptr == QSPI0) {
    NVIC_InitStructure.NVIC_IRQChannel = QSPI0_IRQn;
  } else if (QSPIptr == QSPI1) {
    NVIC_InitStructure.NVIC_IRQChannel = QSPI1_IRQn;
  } else {
    NVIC_InitStructure.NVIC_IRQChannel = QSPI2_IRQn;
  }
  NVIC_InitStructure.NVIC_IRQChannelPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
}

/**
  * @brief  Configures the DMA Channel for QSPI Tx request.  
  * @param QSPIptr: the QUAD spi controller base address.
  * @param  BufferSRC: pointer to the source buffer.
  * @param  bytes: the bytes number for transferring.
  * 
  * @retval 0, okay; else, fail.
  */
int QSPI_LowLevel_DMA_TxConfig(QSPI_TypeDef *QSPIptr, uint8_t *BufferSRC, int bytes)
{
  int QSPIblockwait;
  uint32_t ctrlLReg;
  DMAChannel_TypeDef *TxDMAChannel;
  DMAchannel channel;

  channel = DmaRequestChannel(NULL);
  if ((int)channel >= DMAChannelNum) {
    printf("all dma channel busy\r\n");
    return -1;
  }

  TxDMAChannel = (DMAChannel_TypeDef *)(DMA_BASE + 0x58 * channel);

  ctrlLReg = 0x00000000 + (0 << DMA_CTRL_INT_EN) //INT_EN, ch0 irq disable
             + (0 << DMA_CTRL_DST_TR_WIDTH)      // DST_TR_WIDTH, des transfer width, should set to HSIZE, here is 000, means 8bit
             + (0 << DMA_CTRL_SRC_TR_WIDTH)      // SRC_TR_WIDTH, sor transfer width, should set to HSIZE, here is 000, means 8bit
             + (2 << DMA_CTRL_DINC)              // DINC, des addr increment, des is SPI, so should set to 1x, means no change
             + (0 << DMA_CTRL_SINC)              // SINC, sor addr increment, src is sram, so should set to 00, means to increase
             + (1 << DMA_DEST_MSIZE)             // DEST_MSIZE, des burst length, set to 001 means 4 DST_TR_WIDTH per burst transcation
             + (1 << DMA_SRC_MSIZE)              // SRC_MSIZE, sor burst length, set to 001 means 4 SOR_TR_WIDTH per burst transcation
             + (1 << DMA_TTC_FC)                 // TT_FC,transfer type and flow control,001 means memory to peripheral,dma is flow controller
             + (0 << DMA_DMS)                    // DMS, des master select, 0 means ahb master 0
             + (0 << DMA_SMS)                    // SMS, sor master select, 0 means ahb master 0
             + (0 << DMA_LLP_DST_EN)             // LLP_DST_EN, des block chaining enable, set to 0 disable it
             + (0 << DMA_LLP_SRC_EN);            // LLP_SOR_EN, sor block chaining enable, set to 0 disable it

  TxDMAChannel->SAR = (uint32_t)BufferSRC;
  TxDMAChannel->DAR = (uint32_t)(&QSPIptr->DATA);
  TxDMAChannel->LLP = (uint32_t)0;
  TxDMAChannel->CTL_L = ctrlLReg;
  TxDMAChannel->CTL_H = bytes;
  //now trigger the DMA controller.
  /*Before calling DmaStartRoutine, we must set Channel Configuration Register(CFG) properly.*/

  if (QSPIptr == QSPI0) {
    DmaChannelCFGset(channel, 0, (0x00000000 + (REQ_QSPI0TX << DMA_CFG_SRC_PER) + (REQ_QSPI0TX << DMA_CFG_DEST_PER)));
  } else if (QSPIptr == QSPI1) {
    DmaChannelCFGset(channel, 0, (0x00000000 + (REQ_QSPI1TX << DMA_CFG_SRC_PER) + (REQ_QSPI1TX << DMA_CFG_DEST_PER)));
  } else {
    DmaChannelCFGset(channel, 0, (0x00000000 + (REQ_QSPI2TX << DMA_CFG_SRC_PER) + (REQ_QSPI2TX << DMA_CFG_DEST_PER)));
  }

  if (DmaStartRoutine(channel, 1)) {
    printf("QSPI: DMA routine failed for write.");
    DmaFreeChannel(channel);
    return -1;
  }
  DmaFreeChannel(channel);
  return 0;
}

/**
  * @brief  Configures the DMA Channel for QSPI Rx request.
  * @param QSPIptr: the QUAD spi controller base address.
  * @param  BufferDST: the target store address. 
  * @param  bytes: the bytes number for transferring.
  *
  * @retval 0, okay; else, fail.
  */
int QSPI_LowLevel_DMA_RxConfig(QSPI_TypeDef *QSPIptr, uint8_t *BufferDST, int bytes)
{
  int QSPIblockwait;
  uint32_t ctrlLReg;
  DMAChannel_TypeDef *RxDMAChannel;
  DMAchannel channel;

  if ((((int)BufferDST % 4) != 0) || ((bytes % 4) != 0)) {
    printf("QSPIx Rx not aligned!\n");
    return -2;
  }

  if (bytes > CHANNEL_MAX_SIZE * 4)
    return -1;


  channel = DmaRequestChannel(NULL);
  if ((int)channel >= DMAChannelNum) {
    printf("all dma channel busy\r\n");
    return -1;
  }

  RxDMAChannel = (DMAChannel_TypeDef *)(DMA_BASE + 0x58 * channel);
  ctrlLReg = 0x00000000 + (0 << DMA_CTRL_INT_EN) //INT_EN, ch0 irq disable
             + (2 << DMA_CTRL_DST_TR_WIDTH)      // DST_TR_WIDTH, des transfer width, should set to HSIZE, here is 010, means 32bit
             + (2 << DMA_CTRL_SRC_TR_WIDTH)      // SRC_TR_WIDTH, sor transfer width, should set to HSIZE, here is 010, means 32bit
             + (0 << DMA_CTRL_DINC)              // DINC, des addr increment, des is sram, so should set to 00, means to increase
             + (2 << DMA_CTRL_SINC)              // SINC, sor addr increment, src is SPI, so should set to 1x, means no change
             + (0 << DMA_DEST_MSIZE)             // DEST_MSIZE, des burst length, set to 000 means 1 DST_TR_WIDTH per burst transcation
             + (0 << DMA_SRC_MSIZE)              // SRC_MSIZE, sor burst length, set to 000 means 1 SOR_TR_WIDTH per burst transcation
             + (2 << DMA_TTC_FC)                 // TT_FC,transfer type and flow control,010 means peripheral to memory,dma is flow controller
             + (0 << DMA_DMS)                    // DMS, des master select, 0 means ahb master 0
             + (0 << DMA_SMS)                    // SMS, sor master select, 1 means ahb master 1
             + (0 << DMA_LLP_DST_EN)             // LLP_DST_EN, des block chaining enable, set to 0 disable it
             + (0 << DMA_LLP_SRC_EN);            // LLP_SOR_EN, sor block chaining enable, set to 0 disable it

  RxDMAChannel->SAR = (uint32_t)(&QSPIptr->DATA);
  RxDMAChannel->DAR = (uint32_t)BufferDST;
  RxDMAChannel->LLP = (uint32_t)0;
  RxDMAChannel->CTL_L = ctrlLReg;
  RxDMAChannel->CTL_H = 0x0 + (bytes / 4) + ((bytes % 4) != 0);

  //now trigger the DMA controller.
  /*Before calling DmaStartRoutine, we must set Channel Configuration Register(CFG) properly.*/

  if (QSPIptr == QSPI0) {
    DmaChannelCFGset(channel, 0, (0x00000000 + (9 << DMA_CFG_SRC_PER) + (9 << DMA_CFG_DEST_PER)));
  } else if (QSPIptr == QSPI1) {
    DmaChannelCFGset(channel, 0, (0x00000000 + (11 << DMA_CFG_SRC_PER) + (11 << DMA_CFG_DEST_PER)));
  } else {
    DmaChannelCFGset(channel, 0, (0x00000000 + (15 << DMA_CFG_SRC_PER) + (15 << DMA_CFG_DEST_PER)));
  }

  if (DmaStartRoutine(channel, 1)) {
    printf("QSPI: DMA routine failed for read.");
    DmaFreeChannel(channel);
    return -1;
  }
  DmaFreeChannel(channel);
  return 0;
}
